﻿#include <queue>
#include <map>
#include "connect.h"

//Подключение к базе данных
ShallowSymantic::ShallowSymantic(){
	ret = SQLAllocEnv(&env);
	if( ret != SQL_SUCCESS ){
		return;
	}
	
	ret = SQLAllocConnect(env, &conn);
	if( ret != SQL_SUCCESS ){
		return;
	}
}

ShallowSymantic::~ShallowSymantic(){
	SQLFreeEnv(&env);
	SQLFreeConnect(&conn);
}

bool ShallowSymantic::connect(const std::wstring &database,
				const std::wstring &user,
				const std::wstring &password){
	ret = SQLConnect(conn, (SQLWCHAR*)database.c_str(), SQL_NTS,
		(SQLWCHAR*)user.c_str(), SQL_NTS,
		(SQLWCHAR*)password.c_str(), SQL_NTS);
	
	std::wcout << "connect..." << "\n" << std::endl;

	if( ret != SQL_SUCCESS ){
		std::cerr << "Connect error" << std::endl;
		return false;
	}

	return true;
}

bool ShallowSymantic::disconnect(){
	ret = SQLDisconnect(&conn);
	if( ret != SQL_SUCCESS ){
		return false;
	}
	return true;
}

//Получаем текстовое поле из таблицы
std::wstring ShallowSymantic::retrieveTextField(SQLHSTMT query_stmt, SQLUSMALLINT rowNum)
{
	SQLWCHAR buffer[BUFSIZ] = L"";
	std::wstring result;

	while( ( ret = SQLGetData(query_stmt, rowNum, SQL_C_WCHAR, buffer, BUFSIZ*sizeof(buffer[0]), NULL) ) == SQL_SUCCESS_WITH_INFO ||
		ret == SQL_SUCCESS )
	{
		result += buffer;
		if( result.empty() )
		{
			break;
		}
	}

	return result;
}

//Получаем строку из таблицы objects
bool ShallowSymantic::getObjectsRow( SQLBIGINT id, ShallowSymantic::ObjectsRow * result ){
	if( !result ){
		return false;
	}
	
	wchar_t* query_str = L"select id, upObjId, fullLenCnt, partLenCnt, root from OBJECTS where id = ?";

	SQLHSTMT query_stmt;	//Дескриптор оператора
	SQLAllocStmt(conn, &query_stmt);	//Получение Дескриптора оператора

	//Подготовка запроса
	int ret = SQLPrepare(query_stmt, query_str, SQL_NTS);	
	if ( ret != SQL_SUCCESS ){
		return false;
	}
	
	//Подстановка параметров
	ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
		SQL_BIGINT, SQL_DESC_PRECISION, 0, &id, 0, 0);
	if ( ret != SQL_SUCCESS ){
		return false;
	}

	ret = SQLExecute(query_stmt);
	if ( ret != SQL_SUCCESS ){
		return false;
	}

	ret = SQLFetch(query_stmt);
	if( ret == SQL_SUCCESS){
		ret = SQLGetData(query_stmt, 1, SQL_C_SBIGINT, &result->id, 0,NULL);
		ret = SQLGetData(query_stmt, 2, SQL_C_SBIGINT, &result->upObjId, 0,NULL);
		ret = SQLGetData(query_stmt, 3, SQL_INTEGER, &result->fullLenCnt, 0,NULL);
		ret = SQLGetData(query_stmt, 4, SQL_INTEGER, &result->partLenCnt, 0,NULL);
		ret = SQLGetData(query_stmt, 5, SQL_SMALLINT, &result->root, 0,NULL);
	}

	ret = SQLCloseCursor(query_stmt);

	//Освобождение Дескриптора оператора
	ret = SQLFreeStmt(query_stmt, SQL_DROP);
	return true;
}


//Получаем строку из таблицы graphemas
std::vector<ShallowSymantic::GraphemasRow> ShallowSymantic::getGraphemasRow (SQLBIGINT ownerId){
	std::vector<GraphemasRow> result;
	
	wchar_t* query_str = L"select  id, ownerId, graphema, atributeMode from GRAPHEMAS where ownerId = ?";

	SQLHSTMT query_stmt;	//Дескриптор оператора
	SQLAllocStmt(conn, &query_stmt);	//Получение Дескриптора оператора

	//Подготовка запроса
	int ret = SQLPrepare(query_stmt, query_str, SQL_NTS);	
	if ( ret != SQL_SUCCESS ){
		return result;
	}
	
	//Подстановка параметров
	ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
		SQL_BIGINT, SQL_DESC_PRECISION, 0, &ownerId, 0, 0);
	if ( ret != SQL_SUCCESS ){
		return result;
	}

	ret = SQLExecute(query_stmt);
	if ( ret != SQL_SUCCESS ){
		return result;
	}

	for(;;){
		ret = SQLFetch(query_stmt);
		if(ret == SQL_NO_DATA || ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ){
			// Если больше нет необработанных строк, или произошла ошибка
			break;
		}
		GraphemasRow row;
		ret = SQLGetData(query_stmt, 1, SQL_C_SBIGINT, &row.id, 0,NULL);
		ret = SQLGetData(query_stmt, 2, SQL_C_SBIGINT, &row.ownerId, 0,NULL);
		row.graphema = retrieveTextField( query_stmt, 3 );
		ret = SQLGetData(query_stmt, 4, SQL_INTEGER, &row.attributeMode, 0,NULL);
		result.push_back( row );
	
	}


	ret = SQLCloseCursor(query_stmt);

	//Освобождение Дескриптора оператора
	ret = SQLFreeStmt(query_stmt, SQL_DROP);
	return result;
}

//Получаем строку из таблицы graphemaNormalForms
bool ShallowSymantic::getGraphemaNormalFormsRow ( SQLBIGINT id,
												 ShallowSymantic::GraphemaNormalFormsRow * result )
{	
	if( !result ){
		return false;
	}

	wchar_t* query_str = L"select id, graphId, normalForm, CID, VID from GRAPHEMANORMALFORMS where id = ?";

	SQLHSTMT query_stmt;	//Дескриптор оператора
	SQLAllocStmt(conn, &query_stmt);	//Получение Дескриптора оператора

	//Подготовка запроса
	int ret = SQLPrepare(query_stmt, query_str, SQL_NTS);	
	if ( ret != SQL_SUCCESS ){
		
		return false;
	}
	
	//Подстановка параметров
	ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
		SQL_BIGINT, SQL_DESC_PRECISION, 0, &id, 0, 0);
	if ( ret != SQL_SUCCESS ){
		return false;
	}

	ret = SQLExecute(query_stmt);
	if ( ret != SQL_SUCCESS ){
		return false;
	}

	ret = SQLFetch(query_stmt);
	if( ret == SQL_SUCCESS){
		ret = SQLGetData(query_stmt, 1, SQL_C_SBIGINT, &result->id, 0,NULL);
		ret = SQLGetData(query_stmt, 2, SQL_C_SBIGINT, &result->graphId, 0,NULL);
		result->normalForm = retrieveTextField( query_stmt, 3 );
		ret = SQLGetData(query_stmt, 4, SQL_INTEGER, &result->CID, 0,NULL);
		ret = SQLGetData(query_stmt, 5, SQL_INTEGER, &result->VID, 0,NULL);
	}

	ret = SQLCloseCursor(query_stmt);

	//Освобождение Дескриптора оператора
	ret = SQLFreeStmt(query_stmt, SQL_DROP);
	return true;
}



//получаем строку из таблицы attributes
std::vector<ShallowSymantic::AttributesRow> ShallowSymantic::getAttributesRow( SQLBIGINT id ){
	std::vector<AttributesRow> result;
	
	wchar_t* query_str = L"select id, objId, type from ATTRIBUTES where id = ?";

	SQLHSTMT query_stmt;	//Дескриптор оператора
	SQLAllocStmt(conn, &query_stmt);	//Получение Дескриптора оператора

	//Подготовка запроса
	int ret = SQLPrepare(query_stmt, query_str, SQL_NTS);	
	if ( ret != SQL_SUCCESS ){
		return result;
	}
	
	//Подстановка параметров
	ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
		SQL_BIGINT, SQL_DESC_PRECISION, 0, &id, 0, 0);
	if ( ret != SQL_SUCCESS ){
		return result;
	}

	ret = SQLExecute(query_stmt);
	if ( ret != SQL_SUCCESS ){
		return result;
	}

	for(;;){
		ret = SQLFetch(query_stmt);
		if(ret == SQL_NO_DATA || ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ){
			// Если больше нет необработанных строк, или произошла ошибка
			break;
		}

		AttributesRow row;
		ret = SQLGetData(query_stmt, 1, SQL_C_SBIGINT, &row.id, 0,NULL);
		row.objId = retrieveTextField( query_stmt, 2 );
		//row.normalForm = retrieveTextField( query_stmt, 2 );
		ret = SQLGetData(query_stmt, 3, SQL_SMALLINT, &row.type, 0,NULL);
		
	}

	ret = SQLCloseCursor(query_stmt);
	

	//Освобождение Дескриптора оператора
	ret = SQLFreeStmt(query_stmt, SQL_DROP);
	return result;
}


//Получаем строку из таблицы placement
std::vector<ShallowSymantic::PlacementRow> ShallowSymantic::getPlacementsRow( SQLBIGINT id ){
	std::vector<PlacementRow> result;
	
	wchar_t* query_str = L"select id, placement, type from PLACEMENT where	id = ?";

	SQLHSTMT query_stmt;	//Дескриптор оператора
	SQLAllocStmt(conn, &query_stmt);	//Получение Дескриптора оператора

	//Подготовка запроса
	int ret = SQLPrepare(query_stmt, query_str, SQL_NTS);	
	if ( ret != SQL_SUCCESS ){
		return result;
	}
	
	//Подстановка параметров
	ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
		SQL_BIGINT, SQL_DESC_PRECISION, 0, &id, 0, 0);
	if ( ret != SQL_SUCCESS ){
		return result;
	}

	ret = SQLExecute(query_stmt);
	if ( ret != SQL_SUCCESS ){
		
		return result;
	}
	for(;;){
		ret = SQLFetch(query_stmt);
		if(ret == SQL_NO_DATA || ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ){
			// Если больше нет необработанных строк, или произошла ошибка
			break;
		}

		PlacementRow row;
		ret = SQLGetData(query_stmt, 1, SQL_C_SBIGINT, &row.id, 0,NULL);
		row.placement = retrieveTextField( query_stmt, 2 );
		ret = SQLGetData(query_stmt, 3, SQL_C_SBIGINT, &row.type, 0,NULL);
		result.push_back( row );
	}

	ret = SQLCloseCursor(query_stmt);

	//Освобождение Дескриптора оператора
	ret = SQLFreeStmt(query_stmt, SQL_DROP);
	return result;
}

//Получение отношений объекта по направлению
std::vector<ShallowSymantic::IssRelationsRow> ShallowSymantic::getRelation( SQLBIGINT objId,
																	   ShallowSymantic::RelationDirection type)
{
	std::vector<IssRelationsRow> result;
	
	wchar_t* query_str = L"";
	switch( type ){
		case RelationLeft:
			query_str = L"select fromObjId, toObjId, type from \
						  ISSRELATIONS where fromObjId = ?";
			break;
		case RelationRight:
			query_str = L"select fromObjId, toObjId, type from \
						  ISSRELATIONS where toObjId = ?";
			break;
		case RelationAny:
			query_str = L"select fromObjId, toObjId, relType from \
						  ISSRELATIONS where fromObjId = ? or toObjId = ?";
			break;
	}

	SQLHSTMT query_stmt;	//Дескриптор оператора
	SQLAllocStmt(conn, &query_stmt);	//Получение Дескриптора оператора

	//Подготовка запроса
	int ret = SQLPrepare(query_stmt, query_str, SQL_NTS);	
	if ( ret != SQL_SUCCESS ){
		return result;
	}
	
	//Подстановка параметров
	switch( type ){
		case RelationLeft:
		case RelationRight:
			ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
				SQL_BIGINT, SQL_DESC_PRECISION, 0, &objId, 0, 0);
			break;
		case RelationAny:
			ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
				SQL_BIGINT, SQL_DESC_PRECISION, 0, &objId, 0, 0);
			ret += SQLBindParameter(query_stmt, 2, SQL_PARAM_INPUT, SQL_C_SBIGINT,
				SQL_BIGINT, SQL_DESC_PRECISION, 0, &objId, 0, 0);
			break;
	}
	
	if ( ret != SQL_SUCCESS ){
		return result;
	}

	ret = SQLExecute(query_stmt);
	if ( ret != SQL_SUCCESS ){
		
		return result;
	}
	for(;;){
		ret = SQLFetch(query_stmt);
		if(ret == SQL_NO_DATA || ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ){
			// Если больше нет необработанных строк, или произошла ошибка
			break;
		}

		IssRelationsRow row;
		ret = SQLGetData(query_stmt, 1, SQL_C_SBIGINT, &row.id, 0,NULL);
		ret = SQLGetData(query_stmt, 2, SQL_C_SBIGINT, &row.fromObjId, 0,NULL);
		ret = SQLGetData(query_stmt, 3, SQL_C_SBIGINT, &row.toObjId, 0,NULL);
		ret = SQLGetData(query_stmt, 4, SQL_C_SBIGINT, &row.type, 0,NULL);
		ret = SQLGetData(query_stmt, 5, SQL_C_SBIGINT, &row.cnt, 0,NULL);
		ret = SQLGetData(query_stmt, 6, SQL_C_SBIGINT, &row.possibility, 0,NULL);
		result.push_back( row );
	}

	ret = SQLCloseCursor(query_stmt);

	//Освобождение Дескриптора оператора
	ret = SQLFreeStmt(query_stmt, SQL_DROP);
	return result;
}

//Получение связи между двумя объектами по их идентификаторам
bool ShallowSymantic::getRelation( SQLBIGINT fromObjId, SQLBIGINT toObjId, IssRelationsRow * result ){
	
	wchar_t* query_str = L"select id, fromObjId, toObjId, type, cnt, possibility from \
						  ISSRELATIONS where fromObjId = ? and toObjId = ?";

	SQLHSTMT query_stmt;	//Дескриптор оператора
	SQLAllocStmt(conn, &query_stmt);	//Получение Дескриптора оператора

	//Подготовка запроса
	int ret = SQLPrepare(query_stmt, query_str, SQL_NTS);	
	if ( ret != SQL_SUCCESS ){
		return false;
	}
	
	//Подстановка параметров
	ret = SQLBindParameter(query_stmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT,
		SQL_BIGINT, SQL_DESC_PRECISION, 0, &fromObjId, 0, 0);
	ret = SQLBindParameter(query_stmt, 2, SQL_PARAM_INPUT, SQL_C_SBIGINT,
		SQL_BIGINT, SQL_DESC_PRECISION, 0, &toObjId, 0, 0);
	
	
	if ( ret != SQL_SUCCESS ){
		return false;
	}

	ret = SQLExecute(query_stmt);
	if ( ret != SQL_SUCCESS ){
		return false;
	}

	ret = SQLFetch(query_stmt);
	if(ret == SQL_NO_DATA || ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ){
		return false;
	}

	ret = SQLGetData(query_stmt, 1, SQL_C_SBIGINT, &result->id, 0,NULL);
	ret += SQLGetData(query_stmt, 2, SQL_C_SBIGINT, &result->fromObjId, 0,NULL);
	ret += SQLGetData(query_stmt, 3, SQL_C_SBIGINT, &result->toObjId, 0,NULL);
	ret += SQLGetData(query_stmt, 4, SQL_C_SBIGINT, &result->type, 0,NULL);
	ret += SQLGetData(query_stmt, 5, SQL_C_SBIGINT, &result->cnt, 0,NULL);
	ret += SQLGetData(query_stmt, 5, SQL_C_SBIGINT, &result->possibility, 0,NULL);

	if ( ret != SQL_SUCCESS ){
		return false;
	}

	ret = SQLCloseCursor(query_stmt);

	//Освобождение Дескриптора оператора
	ret = SQLFreeStmt(query_stmt, SQL_DROP);
	return true;
}

//Разбор объекта по графемам и атрибутам 
std::vector< std::wstring > ShallowSymantic::parseObject( SQLBIGINT objId ){

	std::vector<GraphemasRow> graphemasId = getGraphemasRow( objId );
	
	std::vector<std::wstring> result;

	for( size_t i = 0; i < graphemasId.size(); i++ ){
		GraphemaNormalFormsRow normRow;
		if( getGraphemaNormalFormsRow( graphemasId[i].id, &normRow ) ){
			result.push_back( normRow.normalForm );
		}
	}

	return result;
}

//Разбор цепочки объектов
std::vector< std::wstring > ShallowSymantic::parseChain( const std::vector<SQLBIGINT> & chain ){
	
	std::vector< std::wstring > result;

	for( size_t i = 0; i < chain.size(); i++ ){
		std::vector< std::wstring > parsedObject = parseObject( chain[i] );
		result.insert( result.end(), parsedObject.begin(), parsedObject.end() );
	}
	
	return result;
}

std::vector< SQLBIGINT > ShallowSymantic::searchRelations( SQLBIGINT rootObjId,
														  RelationDirection dir,
														  SQLBIGINT targetObjId)
{
	if( targetObjId == undefinedId ){
		return search( rootObjId, dir );
	}else{
		return searchPath( rootObjId, targetObjId, dir );
	}
}



// Реализация поиска в ширину всех связанных объектов по графу отношений
std::vector<SQLBIGINT> ShallowSymantic::search( SQLBIGINT rootObjId,
											   RelationDirection dir)
{
	std::vector< SQLBIGINT > result;

	// Рабочая очередь
	std::queue< SQLBIGINT > workQueue;

	workQueue.push( rootObjId );

	while( !workQueue.empty() ){

		SQLBIGINT currentId = workQueue.front();
		workQueue.pop();

		std::vector< IssRelationsRow > rels = getRelation( currentId, dir );

		for( size_t i = 0; i < rels.size(); ++i ){
			
			SQLBIGINT pushId = undefinedId;

			switch( dir ){
			case RelationLeft:
				pushId = rels[i].fromObjId;
				break;

			case RelationRight:
				pushId = rels[i].toObjId;
				break;

			case RelationAny:
				if( currentId == rels[i].toObjId ){
					pushId = rels[i].fromObjId;
				}else{
					pushId = rels[i].toObjId;
				}
				break;
			}

			if( pushId != rootObjId && std::find( result.begin(), result.end(), pushId ) == result.end() ){
				workQueue.push( pushId );
				result.push_back( pushId );
			}
		}
	}

	return result;
}

// Реализация поиска пути в ширину по графу отношений
std::vector<SQLBIGINT> ShallowSymantic::searchPath( SQLBIGINT rootObjId,
														SQLBIGINT targetObjId,
														RelationDirection dir )
{
	typedef std::vector< SQLBIGINT > Path;
	typedef std::pair<
		SQLBIGINT,	// Идентификатор объекта
		Path		// Путь до этого объекта
	> MapItem;
	typedef std::map< SQLBIGINT, Path > PathMap;

	// Все вершины с путями
	PathMap objects;
	// Корневая вершина с пустым путем
	objects.insert( MapItem( rootObjId, Path() ) );

	// Рабочая очередь
	std::queue< SQLBIGINT > workQueue;
	workQueue.push( rootObjId );

	while( !workQueue.empty() ){

		SQLBIGINT currentId = workQueue.front();
		PathMap::iterator currentPos = objects.find( currentId );

		workQueue.pop();

		std::vector< IssRelationsRow > rels = getRelation( currentId, dir );

		for( size_t i = 0; i < rels.size(); ++i ){
			
			SQLBIGINT pushId = undefinedId;

			switch( dir ){
			case RelationLeft:
				pushId = rels[i].fromObjId;
				break;

			case RelationRight:
				pushId = rels[i].toObjId;
				break;

			case RelationAny:
				if( currentId == rels[i].toObjId ){
					pushId = rels[i].fromObjId;
				}else{
					pushId = rels[i].toObjId;
				}
				break;
			}

			if( pushId == rootObjId ){
				continue;
			}

			PathMap::iterator pushPos = objects.find( pushId );
			if( pushPos == objects.end() ){
				// Если для объекта еще не найден путь
				objects.insert( MapItem( pushId, objects.find( currentId )->second ) );
				pushPos = objects.find( pushId );
				pushPos->second.push_back( currentId );
				workQueue.push( pushId );

			}else if( pushPos->second.size() > currentPos->second.size() ){
				// Если текущий путь менее оптимален
				pushPos->second.push_back( currentId );

			}

			if( pushId == targetObjId ){
				pushPos->second.push_back( pushId );
				return pushPos->second;
			}
		}
	}

	return Path();
}